package org.sooth.task;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.sooth.utils.ClassPathModificator;
import org.sooth.utils.HibernateUtil;

/* TODO enable annotation support */
/* TODO improve query parser: enable checking parameterized queries */
/* TODO improve query parser: insert dummy values */
/* TODO improve query parser: fetch queries if using StringBuffer */
/* TODO file and line number report */

public class SoothTask extends Task  {

	private final FilenameFilter sourceFilesFilter = new FilenameFilter(){
		@Override
		public boolean accept(File file, String name) {
			if (name.indexOf(".java") > -1)
				return true;
			return false;
		}
	};

	private final FilenameFilter dirsFilter = new FilenameFilter(){
		@Override
		public boolean accept(File file, String name) {
			if (!file.isFile())
				return true;
			return false;
		}
	};

    private String hibernateConfig;
    private String src;
    private boolean exception = true;
    private boolean sendToDB = true;

	public void execute() throws BuildException {


    	if (getHibernateConfig() == null)
    		setHibernateConfig("hibernate.cfg.xml");

    	Project project = getProject();
    	File baseDir = project.getBaseDir();
    	File sourceDir = new File(baseDir.getAbsoluteFile() + "/src");
    	File binDir = new File(baseDir.getAbsoluteFile() + "/bin");

    	// Loading the resource dynamically
    	try {
    		ClassPathModificator.addFile(baseDir);
    		ClassPathModificator.addFile(sourceDir);
    		ClassPathModificator.addFile(binDir);
    	} catch (IOException ex){
    		throw new BuildException(ex);
    	}


    	HibernateUtil.init(getHibernateConfig());
    	List<File> files = fetchSourceFiles(baseDir);
    	for (File file: files){
    		validate(file);
    	}

    }

    private void validate(File file) throws BuildException {
    	try {
    		String contents = getContents(file);
    		List<String> queries = fetchQueries(contents);
    		Session session = HibernateUtil.getSession();
    		session.beginTransaction();
    		for (String query: queries){
    			log("Validating query: " + query);
    			Query q = session.createQuery(query);
    			if (isSendToDB())
    				q.list();
    		}
    		session.getTransaction().commit();

    	} catch (HibernateException ex){
    		log("Error: " + ex.getMessage());
    		throw new BuildException(ex);
    	} catch (IOException ex){
    		log("Error getting contents of a file");
    		throw new BuildException(ex);
    	}
    }

    private String getContents(File file) throws FileNotFoundException, IOException {
    	BufferedReader input = new BufferedReader(new FileReader(file));

   	 	StringBuffer contents = new StringBuffer();
        String line;
        while (( line = input.readLine()) != null){
            contents.append(line);
            contents.append(System.getProperty("line.separator"));
        }
        input.close();

        return contents.toString();
    }

    private List<String> fetchQueries(String content){
    	List<String> queries = new LinkedList<String>();
    	String[] parts = content.split("\"from ");

    	for (int i = 1; i < parts.length; i ++){
    		String[] subparts = parts[i].split(";");

    		// Trimming trailing characters
    		if (subparts[0].charAt(subparts[0].length() - 1) == ')')
    			subparts[0] = subparts[0].substring(0, subparts[0].length() - 1);
    		if (subparts[0].charAt(subparts[0].length() - 1) == '\"')
    			subparts[0] = subparts[0].substring(0, subparts[0].length() - 1);

    		queries.add("from " + subparts[0]);
    	}

    	return queries;


    }

    private  List<File> fetchSourceFiles(File file){

    	List<File> files = new LinkedList<File>();
    	List<File> dirs = new LinkedList<File>();
		if (!file.isFile()){
    		files.addAll(Arrays.asList(file.listFiles(sourceFilesFilter)));
    		dirs = Arrays.asList(file.listFiles(dirsFilter));
    	}
		for (File dir: dirs){
			files.addAll(fetchSourceFiles(dir));
		}

		return files;
    }

    public String getHibernateConfig() {
        return hibernateConfig;
    }
    public void setHibernateConfig(String hibernateConfig) {
        this.hibernateConfig = hibernateConfig;
    }
    public boolean isException() {
        return exception;
    }
    public void setException(boolean exception) {
        this.exception = exception;
    }
	public String getSrc() {
		return src;
	}
	public void setSrc(String src) {
		this.src = src;
	}
    public boolean isSendToDB() {
		return sendToDB;
	}
	public void setSendToDB(boolean sendToDB) {
		this.sendToDB = sendToDB;
	}
}